#! python3
# -*- utf-8 -*-
from sklearn import neighbors
import numpy as np
import os

"""
下面是中国大学MOOC上，北理工《python机器学习应用》第6单元手写数字识别实例所用代码，和MOOC上的一致，只改了些变量名而已。

课程上说其使用的是DBRHD数据集，但是我按照老师给的网址（https://archive.ics.uci.edu/ml/datasets/PenBased+Recognition+of+Handwritten+Digits）
去下载，得到的数据集内容却和在老师视频里看到的大相径庭。该文件中数据集是在课程“公告”中，
老师给出的百度云链接（https://pan.baidu.com/s/1eR7doh8）上下载的，暂且称之为“北理工数据集”吧。
这个数据集内容和MOOC视频里看到的一样。数据集在digits目录下。

使用该数据集中测试集的数据测试，k取值1到7之间，都有97%以上的正确率。但是用h5 + JS写了
个前端，在手机上获取手写笔迹后将图片上传回服务器，使用漫水法裁剪出数字所在区域，经缩放
等操作处理为32x32大小的图片，使图片尽量接近训练集。但是识别的正确率却极低。数字“3”易被
识别成“7”“1”和“9”，正确率在35%左右.数字“2”的正确率较高在60%-70%左右。将笔迹
加粗，识别效果似乎有一点点改善，未作量化验证，但是感觉几乎没有变化。推测使用测试集时识别
正确率高，而实际图片正确率却很低的原因是实际测试时获得的图片与原数据集还是有较大差别。
例如训练集中数字“3”大都比较宽，左右两侧的空白大约是3像素，但是实际图片上数字“3”
比较瘦长，左右空白可能有5像素。而测试集和训练集的来源和处理方式相同，图片几乎相同，所以
正确率很高。如果比较刻意地把“3”写得宽些，识别的正确率立刻有显著提高，但是显然我们不应该
要求用户在输入时还要刻意注意写宽些来提高正确率。

2019-2-13
"""


N_NEIGHBOR = 3
PATH = os.path.join('dataSet', 'digits')  # data set path


def img2vector(fileName):
    retMat = np.zeros((1024,), int)

    with open(fileName, 'r') as f:
        lines = f.readlines()
        for i in range(32):
            for j in range(32):
                retMat[i*32+j] = int(lines[i][j])

    return retMat


def readDataSet(path):
    file_list = os.listdir(path)
    num_files = len(file_list)
    data_set = np.zeros([num_files, 1024], int)
    hw_labels = np.zeros([num_files])

    for i in range(num_files):
        file_name = file_list[i]
        digit = int(file_name.split('_')[0])
        hw_labels[i] = digit
        data_set[i] = img2vector(os.path.join(path, file_name))

    return data_set, hw_labels


def predict(data):
    return knn.predict(data)


print('training...')
train_data_set, train_hw_labels = readDataSet(os.path.join(PATH, 'trainingDigits'))

knn = neighbors.KNeighborsClassifier(n_neighbors=N_NEIGHBOR, algorithm='brute')
knn.fit(train_data_set, train_hw_labels)
print('training finish !')


if __name__ == '__main__':
    print('testing...')
    test_data_set, test_hw_labels = readDataSet(os.path.join(PATH, 'testDigits'))
    res = knn.predict(test_data_set)
    error_num = np.sum(res != test_hw_labels)
    num = len(test_data_set)

    print('Total num:', num,
          "\nWrong num:", error_num,
          '\nWrong rate:', error_num/float(num),
          '\nAccuracy:', 1-error_num/float(num))




